"""
降级策略管理器 - 模块失效时的降级处理

这个模块实现了降级策略管理：
- 多级降级策略
- 自动降级触发
- 降级策略配置
- 服务健康检查
- 降级恢复机制
"""

import asyncio
import logging
import time
from dataclasses import dataclass, field
from datetime import datetime, timezone, timedelta
from enum import Enum
from typing import Any, Callable, Dict, List, Optional, Union, Set
import json

from circuit_breaker import CircuitBreaker, CircuitBreakerConfig, get_circuit_breaker_manager

logger = logging.getLogger(__name__)


class DegradationLevel(Enum):
    """降级级别"""
    NORMAL = "normal"          # 正常状态
    LIGHT = "light"            # 轻度降级
    MEDIUM = "medium"          # 中度降级
    HEAVY = "heavy"            # 重度降级
    EMERGENCY = "emergency"    # 紧急降级


class ServiceStatus(Enum):
    """服务状态"""
    HEALTHY = "healthy"        # 健康
    DEGRADED = "degraded"      # 降级
    UNHEALTHY = "unhealthy"    # 不健康
    OFFLINE = "offline"        # 离线


@dataclass
class ServiceHealth:
    """服务健康状态"""
    service_name: str
    status: ServiceStatus
    response_time: float
    success_rate: float
    last_check: datetime
    error_count: int = 0
    consecutive_failures: int = 0
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class DegradationRule:
    """降级规则"""
    name: str
    service_name: str
    condition: str  # 条件表达式
    target_level: DegradationLevel
    cooldown: int = 300  # 冷却时间（秒）
    enabled: bool = True
    metadata: Dict[str, Any] = field(default_factory=dict)


@dataclass
class DegradationAction:
    """降级动作"""
    name: str
    service_name: str
    level: DegradationLevel
    action_type: str  # "disable", "reduce", "fallback", "timeout"
    parameters: Dict[str, Any] = field(default_factory=dict)
    enabled: bool = True


@dataclass
class DegradationConfig:
    """降级配置"""
    # 健康检查配置
    health_check_interval: int = 30  # 健康检查间隔（秒）
    health_check_timeout: float = 5.0  # 健康检查超时（秒）
    
    # 降级触发条件
    response_time_threshold: float = 2.0  # 响应时间阈值（秒）
    success_rate_threshold: float = 0.8  # 成功率阈值
    consecutive_failure_threshold: int = 5  # 连续失败阈值
    
    # 恢复条件
    recovery_response_time: float = 1.0  # 恢复响应时间阈值（秒）
    recovery_success_rate: float = 0.95  # 恢复成功率阈值
    recovery_check_count: int = 3  # 恢复检查次数
    
    # 全局配置
    enable_auto_degradation: bool = True  # 启用自动降级
    enable_auto_recovery: bool = True  # 启用自动恢复
    max_degradation_level: DegradationLevel = DegradationLevel.EMERGENCY


class DegradationManager:
    """降级策略管理器"""
    
    def __init__(self, config: Optional[DegradationConfig] = None):
        self.config = config or DegradationConfig()
        
        # 服务状态管理
        self.service_health: Dict[str, ServiceHealth] = {}
        self.service_degradation_level: Dict[str, DegradationLevel] = {}
        
        # 降级规则和动作
        self.degradation_rules: List[DegradationRule] = []
        self.degradation_actions: List[DegradationAction] = []
        
        # 熔断器管理
        self.circuit_breakers: Dict[str, CircuitBreaker] = {}
        self.circuit_breaker_manager = get_circuit_breaker_manager()
        
        # 运行状态
        self.is_running = False
        self.health_check_task: Optional[asyncio.Task] = None
        self.degradation_check_task: Optional[asyncio.Task] = None
        
        # 锁
        self.lock = asyncio.Lock()
        
        self.logger = logging.getLogger(__name__)
        self.logger.info("降级策略管理器已初始化")
    
    async def start(self) -> None:
        """启动降级管理器"""
        if self.is_running:
            return
        
        self.is_running = True
        
        # 启动健康检查任务
        self.health_check_task = asyncio.create_task(self._health_check_worker())
        
        # 启动降级检查任务
        self.degradation_check_task = asyncio.create_task(self._degradation_check_worker())
        
        self.logger.info("降级策略管理器已启动")
    
    async def stop(self) -> None:
        """停止降级管理器"""
        if not self.is_running:
            return
        
        self.is_running = False
        
        # 停止任务
        if self.health_check_task:
            self.health_check_task.cancel()
            try:
                await self.health_check_task
            except asyncio.CancelledError:
                pass
        
        if self.degradation_check_task:
            self.degradation_check_task.cancel()
            try:
                await self.degradation_check_task
            except asyncio.CancelledError:
                pass
        
        self.logger.info("降级策略管理器已停止")
    
    async def register_service(self, 
                              service_name: str,
                              health_check_func: Optional[Callable] = None,
                              initial_level: DegradationLevel = DegradationLevel.NORMAL) -> None:
        """注册服务"""
        async with self.lock:
            # 初始化服务健康状态
            self.service_health[service_name] = ServiceHealth(
                service_name=service_name,
                status=ServiceStatus.HEALTHY,
                response_time=0.0,
                success_rate=1.0,
                last_check=datetime.now(timezone.utc)
            )
            
            # 设置初始降级级别
            self.service_degradation_level[service_name] = initial_level
            
            # 创建熔断器
            circuit_config = CircuitBreakerConfig(
                failure_threshold=3,
                failure_rate_threshold=0.5,
                recovery_timeout=30,
                request_timeout=self.config.health_check_timeout
            )
            
            self.circuit_breakers[service_name] = await self.circuit_breaker_manager.get_circuit_breaker(
                f"degradation_{service_name}",
                config=circuit_config
            )
            
            self.logger.info(f"已注册服务: {service_name}")
    
    async def add_degradation_rule(self, rule: DegradationRule) -> None:
        """添加降级规则"""
        async with self.lock:
            self.degradation_rules.append(rule)
            self.logger.info(f"已添加降级规则: {rule.name}")
    
    async def add_degradation_action(self, action: DegradationAction) -> None:
        """添加降级动作"""
        async with self.lock:
            self.degradation_actions.append(action)
            self.logger.info(f"已添加降级动作: {action.name}")
    
    async def get_service_degradation_level(self, service_name: str) -> DegradationLevel:
        """获取服务降级级别"""
        return self.service_degradation_level.get(service_name, DegradationLevel.NORMAL)
    
    async def set_service_degradation_level(self, 
                                           service_name: str,
                                           level: DegradationLevel,
                                           reason: str = "手动设置") -> None:
        """设置服务降级级别"""
        async with self.lock:
            old_level = self.service_degradation_level.get(service_name, DegradationLevel.NORMAL)
            self.service_degradation_level[service_name] = level
            
            # 执行降级动作
            await self._execute_degradation_actions(service_name, level)
            
            self.logger.info(
                f"服务 {service_name} 降级级别变更: {old_level.value} -> {level.value}, 原因: {reason}"
            )
    
    async def _health_check_worker(self) -> None:
        """健康检查工作线程"""
        while self.is_running:
            try:
                await self._perform_health_checks()
                await asyncio.sleep(self.config.health_check_interval)
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"健康检查异常: {e}")
                await asyncio.sleep(5)
    
    async def _degradation_check_worker(self) -> None:
        """降级检查工作线程"""
        while self.is_running:
            try:
                await self._check_degradation_rules()
                await asyncio.sleep(10)  # 每10秒检查一次
            except asyncio.CancelledError:
                break
            except Exception as e:
                self.logger.error(f"降级检查异常: {e}")
                await asyncio.sleep(5)
    
    async def _perform_health_checks(self) -> None:
        """执行健康检查"""
        for service_name in self.service_health.keys():
            try:
                await self._check_service_health(service_name)
            except Exception as e:
                self.logger.error(f"服务 {service_name} 健康检查失败: {e}")
    
    async def _check_service_health(self, service_name: str) -> None:
        """检查单个服务健康状态"""
        start_time = time.time()
        
        try:
            # 这里应该调用实际的服务健康检查
            # 目前使用模拟的健康检查
            await asyncio.sleep(0.1)  # 模拟检查时间
            
            response_time = time.time() - start_time
            success = True
            
            # 更新健康状态
            health = self.service_health[service_name]
            health.response_time = response_time
            health.last_check = datetime.now(timezone.utc)
            
            if success:
                health.consecutive_failures = 0
                health.status = ServiceStatus.HEALTHY
            else:
                health.consecutive_failures += 1
                health.error_count += 1
                if health.consecutive_failures >= self.config.consecutive_failure_threshold:
                    health.status = ServiceStatus.UNHEALTHY
                else:
                    health.status = ServiceStatus.DEGRADED
            
            # 更新成功率（简化计算）
            if health.consecutive_failures == 0:
                health.success_rate = 1.0
            else:
                health.success_rate = max(0.0, 1.0 - (health.consecutive_failures * 0.1))
            
        except Exception as e:
            # 健康检查失败
            response_time = time.time() - start_time
            health = self.service_health[service_name]
            health.response_time = response_time
            health.last_check = datetime.now(timezone.utc)
            health.consecutive_failures += 1
            health.error_count += 1
            health.success_rate = max(0.0, 1.0 - (health.consecutive_failures * 0.1))
            health.status = ServiceStatus.UNHEALTHY
            
            self.logger.warning(f"服务 {service_name} 健康检查失败: {e}")
    
    async def _check_degradation_rules(self) -> None:
        """检查降级规则"""
        if not self.config.enable_auto_degradation:
            return
        
        for rule in self.degradation_rules:
            if not rule.enabled:
                continue
            
            try:
                if await self._evaluate_rule(rule):
                    await self._trigger_degradation(rule)
            except Exception as e:
                self.logger.error(f"检查降级规则 {rule.name} 失败: {e}")
    
    async def _evaluate_rule(self, rule: DegradationRule) -> bool:
        """评估降级规则"""
        service_name = rule.service_name
        
        if service_name not in self.service_health:
            return False
        
        health = self.service_health[service_name]
        
        # 简单的条件评估（实际应该使用更复杂的表达式引擎）
        if rule.condition == "response_time_high":
            return health.response_time > self.config.response_time_threshold
        elif rule.condition == "success_rate_low":
            return health.success_rate < self.config.success_rate_threshold
        elif rule.condition == "consecutive_failures":
            return health.consecutive_failures >= self.config.consecutive_failure_threshold
        elif rule.condition == "service_unhealthy":
            return health.status == ServiceStatus.UNHEALTHY
        
        return False
    
    async def _trigger_degradation(self, rule: DegradationRule) -> None:
        """触发降级"""
        service_name = rule.service_name
        current_level = await self.get_service_degradation_level(service_name)
        
        # 检查是否应该降级
        if self._should_degrade(current_level, rule.target_level):
            await self.set_service_degradation_level(
                service_name,
                rule.target_level,
                f"规则触发: {rule.name}"
            )
    
    def _should_degrade(self, current_level: DegradationLevel, target_level: DegradationLevel) -> bool:
        """判断是否应该降级"""
        level_order = {
            DegradationLevel.NORMAL: 0,
            DegradationLevel.LIGHT: 1,
            DegradationLevel.MEDIUM: 2,
            DegradationLevel.HEAVY: 3,
            DegradationLevel.EMERGENCY: 4
        }
        
        return level_order[target_level] > level_order[current_level]
    
    async def _execute_degradation_actions(self, service_name: str, level: DegradationLevel) -> None:
        """执行降级动作"""
        for action in self.degradation_actions:
            if (action.service_name == service_name and 
                action.level == level and 
                action.enabled):
                
                try:
                    await self._execute_action(action)
                except Exception as e:
                    self.logger.error(f"执行降级动作 {action.name} 失败: {e}")
    
    async def _execute_action(self, action: DegradationAction) -> None:
        """执行单个降级动作"""
        if action.action_type == "disable":
            # 禁用服务
            await self._disable_service(action.service_name)
        elif action.action_type == "reduce":
            # 减少服务容量
            await self._reduce_service_capacity(action.service_name, action.parameters)
        elif action.action_type == "fallback":
            # 启用降级处理
            await self._enable_fallback(action.service_name, action.parameters)
        elif action.action_type == "timeout":
            # 调整超时时间
            await self._adjust_timeout(action.service_name, action.parameters)
        
        self.logger.info(f"已执行降级动作: {action.name}")
    
    async def _disable_service(self, service_name: str) -> None:
        """禁用服务"""
        if service_name in self.circuit_breakers:
            await self.circuit_breakers[service_name].force_open("服务被禁用")
    
    async def _reduce_service_capacity(self, service_name: str, parameters: Dict[str, Any]) -> None:
        """减少服务容量"""
        # 这里可以实现容量减少逻辑
        self.logger.info(f"服务 {service_name} 容量减少: {parameters}")
    
    async def _enable_fallback(self, service_name: str, parameters: Dict[str, Any]) -> None:
        """启用降级处理"""
        # 这里可以实现降级处理逻辑
        self.logger.info(f"服务 {service_name} 启用降级处理: {parameters}")
    
    async def _adjust_timeout(self, service_name: str, parameters: Dict[str, Any]) -> None:
        """调整超时时间"""
        timeout = parameters.get("timeout", 5.0)
        if service_name in self.circuit_breakers:
            self.circuit_breakers[service_name].config.request_timeout = timeout
    
    async def get_degradation_status(self) -> Dict[str, Any]:
        """获取降级状态"""
        status = {
            "services": {},
            "rules": len(self.degradation_rules),
            "actions": len(self.degradation_actions),
            "is_running": self.is_running
        }
        
        for service_name, health in self.service_health.items():
            degradation_level = await self.get_service_degradation_level(service_name)
            status["services"][service_name] = {
                "health": {
                    "status": health.status.value,
                    "response_time": health.response_time,
                    "success_rate": health.success_rate,
                    "consecutive_failures": health.consecutive_failures,
                    "last_check": health.last_check.isoformat()
                },
                "degradation_level": degradation_level.value
            }
        
        return status
    
    async def reset_service_degradation(self, service_name: str) -> None:
        """重置服务降级状态"""
        async with self.lock:
            if service_name in self.service_degradation_level:
                self.service_degradation_level[service_name] = DegradationLevel.NORMAL
            
            if service_name in self.circuit_breakers:
                await self.circuit_breakers[service_name].reset()
            
            self.logger.info(f"已重置服务 {service_name} 的降级状态")


# 全局降级管理器
_global_degradation_manager: Optional[DegradationManager] = None


def get_degradation_manager() -> DegradationManager:
    """获取全局降级管理器"""
    global _global_degradation_manager
    if _global_degradation_manager is None:
        _global_degradation_manager = DegradationManager()
    return _global_degradation_manager
